<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>分块文件上传测试</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; background-color: #f9f9f9; color: #333; }
        .container { background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); max-width: 600px; margin: auto; }
        h1 { text-align: center; color: #007bff; }
        label { display: block; margin-top: 15px; margin-bottom: 5px; font-weight: bold; }
        input[type="file"], input[type="number"], button {
            width: calc(100% - 22px); /* Account for padding */
            padding: 10px;
            margin-bottom: 15px;
            border-radius: 5px;
            border: 1px solid #ddd;
            box-sizing: border-box;
        }
        input[type="number"] { width: 100px; }
        button {
            background-color: #007bff;
            color: white;
            border: none;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s ease;
        }
        button:hover { background-color: #0056b3; }
        button:disabled { background-color: #ccc; cursor: not-allowed; }
        .progress-bar-container { width: 100%; background-color: #e9ecef; border-radius: 5px; margin-top: 20px; overflow: hidden; }
        .progress-bar {
            width: 0%;
            height: 25px;
            background-color: #28a745;
            text-align: center;
            line-height: 25px;
            color: white;
            border-radius: 5px;
            transition: width 0.4s ease;
        }
        #status { margin-top: 20px; padding: 12px; border-radius: 5px; font-size: 0.95em; }
        .status-success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
        .status-error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
        .status-info { background-color: #e2e3e5; color: #383d41; border: 1px solid #d6d8db; }
        .status-pending { background-color: #fff3cd; color: #856404; border: 1px solid #ffeeba; }
        #fileInfo { margin-top: 15px; font-size: 0.9em; color: #555; }
    </style>
</head>
<body>
    <div class="container">
        <h1>分块文件上传</h1>
        <p>此页面用于测试后端的分块文件上传和合并功能。</p>

        <label for="fileInput">选择文件:</label>
        <input type="file" id="fileInput">
        <div id="fileInfo"></div>

        <label for="chunkSizeInput">分块大小 (MB):</label>
        <input type="number" id="chunkSizeInput" value="2" min="1" max="14">
        <small>请确保分块大小小于后端配置的单个文件最大限制 (默认为15MB)。</small>
        <br><br>

        <button id="uploadButton">开始上传</button>

        <div class="progress-bar-container">
            <div class="progress-bar" id="progressBar">0%</div>
        </div>
        <div id="status" class="status-info">请选择文件并点击上传。</div>
    </div>

    <script>
        const fileInput = document.getElementById('fileInput');
        const uploadButton = document.getElementById('uploadButton');
        const progressBar = document.getElementById('progressBar');
        const statusDiv = document.getElementById('status');
        const chunkSizeInput = document.getElementById('chunkSizeInput');
        const fileInfoDiv = document.getElementById('fileInfo');

        // 后端分块上传接口地址 (根据您的 FileController 配置)
        const UPLOAD_CHUNK_URL = 'http://localhost:8080/file/upload';

        fileInput.addEventListener('change', () => {
            const file = fileInput.files[0];
            if (file) {
                fileInfoDiv.textContent = `已选择文件: ${file.name} (大小: ${(file.size / 1024 / 1024).toFixed(2)} MB)`;
            } else {
                fileInfoDiv.textContent = '';
            }
        });

        uploadButton.addEventListener('click', async () => {
            const file = fileInput.files[0];
            const CHUNK_SIZE_MB_CONFIG = parseInt(chunkSizeInput.value, 10);

            if (!file) {
                updateStatus('错误：请先选择一个文件！', 'error');
                return;
            }
            if (isNaN(CHUNK_SIZE_MB_CONFIG) || CHUNK_SIZE_MB_CONFIG <= 0 || CHUNK_SIZE_MB_CONFIG >= 15) { // spring.servlet.multipart.max-file-size 默认为15MB
                updateStatus('错误：无效的分块大小。必须是 1 到 14 之间的数字 (MB)。', 'error');
                return;
            }

            const CHUNK_SIZE = CHUNK_SIZE_MB_CONFIG * 1024 * 1024; // 转换为字节

            uploadButton.disabled = true;
            updateStatus('正在准备上传...', 'info');
            progressBar.style.width = '0%';
            progressBar.textContent = '0%';

            // 生成文件唯一标识符 (可以使用更复杂的方式，如结合用户ID等)
            // 为简单起见，这里使用 文件名-大小-最后修改时间戳
            const fileIdentifier = `${file.name}-${file.size}-${file.lastModified}`;
            const originalFilename = file.name;
            const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
            let uploadedChunks = 0;
            let isMerging = false;

            console.log(`文件信息: ${originalFilename}, 大小: ${file.size} bytes`);
            console.log(`分块大小: ${CHUNK_SIZE} bytes, 总块数: ${totalChunks}`);
            console.log(`文件标识符: ${fileIdentifier}`);

            for (let i = 0; i < totalChunks; i++) {
                const start = i * CHUNK_SIZE;
                const end = Math.min(start + CHUNK_SIZE, file.size);
                const chunk = file.slice(start, end);

                const formData = new FormData();
                formData.append('file', chunk); // 后端 @RequestParam("file")
                formData.append('fileIdentifier', fileIdentifier);
                formData.append('chunkNumber', i);
                formData.append('totalChunks', totalChunks);
                formData.append('originalFilename', originalFilename);

                updateStatus(`正在上传块 ${i + 1} / ${totalChunks} (${(chunk.size / 1024 / 1024).toFixed(2)} MB)...`, 'info');
                console.log(`上传块 ${i}: from ${start} to ${end-1}`);

                try {
                    const response = await fetch(UPLOAD_CHUNK_URL, {
                        method: 'POST',
                        body: formData
                        // Content-Type 会由浏览器根据 FormData 自动设置
                    });

                    const result = await response.json(); // 解析后端返回的Result对象

                    if (response.ok && result.code === 200) {
                        console.log(`块 ${i + 1} 响应:`, result);
                        uploadedChunks++;
                        const progress = Math.round((uploadedChunks / totalChunks) * 100);
                        progressBar.style.width = progress + '%';
                        progressBar.textContent = progress + '%';

                        if (result.message.includes("文件上传并合并成功")) {
                            isMerging = false; // 应该已经合并完了
                            updateStatus(`文件上传并合并成功！<br>文件名: ${originalFilename}<br>下载链接 (示例): <a href="${result.data}" target="_blank">${result.data}</a>`, 'success');
                            // 注意: 此下载链接需要后端有 /download/{filename} 这样的接口支持
                        } else if (result.message.includes("块上传成功") && uploadedChunks < totalChunks) {
                            // 块成功，等待下一块
                        } else if (uploadedChunks === totalChunks && !result.message.includes("文件上传并合并成功")) {
                            // 所有块都发送了，但后端还没有返回合并成功的消息 (可能在处理最后一个块时合并)
                            // 这里后端会在最后一个块上传成功时直接返回合并结果
                            // 如果后端逻辑是接收完所有块后再单独发一个合并请求，这里逻辑会不同
                            isMerging = true;
                            updateStatus(`所有块已上传。等待服务器合并文件...`, 'pending');
                        }

                    } else {
                        console.error(`块 ${i + 1} 上传失败:`, result);
                        updateStatus(`错误：块 ${i + 1} 上传失败 - ${result.message || response.statusText}`, 'error');
                        uploadButton.disabled = false;
                        return; // 停止上传
                    }
                } catch (error) {
                    console.error(`块 ${i + 1} 上传网络错误:`, error);
                    updateStatus(`错误：块 ${i + 1} 上传发生网络错误 - ${error.message}`, 'error');
                    uploadButton.disabled = false;
                    return; // 停止上传
                }
            }

            if (uploadedChunks === totalChunks && !isMerging) {
                 // 如果循环结束了，但没有明确的合并成功消息（理论上合并成功会在最后一个块返回时就包含）
                 // 这通常表示所有块都发送了，并且最后一个块的响应就是合并成功的响应。
                 // 如果上面的 if (result.message.includes("文件上传并合并成功")) 已处理，这里可能不会执行。
                 // 为了保险，可以再检查一次。
                 // 实际上，如果最后一个块成功并触发合并，上面的逻辑已经覆盖了。
            }
            uploadButton.disabled = false;
        });

        function updateStatus(message, type) {
            // statusDiv.textContent = message; // 避免HTML注入，如果message包含HTML，则用innerHTML
            statusDiv.innerHTML = message; // 使用innerHTML以支持HTML标签如 <br> 和 <a>
            statusDiv.className = `status-${type}`; // 'status-success', 'status-error', 'status-info', 'status-pending'
        }

        // 初始状态
        updateStatus('请选择文件，设置分块大小，然后点击“开始上传”。', 'info');
    </script>
</body>
</html>